#include "EPollPoller.h"
#include "Logger.h"
#include "Channel.h"
#include <errno.h> 
#include <unistd.h>
#include <string.h>

const int kNew = -1;    //channel的 index_ 初始化就是-1
const int kAdded = 1;
const int kDeleted = 2;

/*
    epoll_create(int size):參數沒有意義，但是必須傳入一個大於0的數
    epoll_create1(int flags)
        flag == 0             : 和epoll_create差不多
        flag == EPOLL_CLOEXEC : 父進程使用epoll_create1獲得的epoll_fd，如果此時父進程fork一下，
                                然後使用exec去替換子進程，此時子進程會將父進程中設置的fd關閉
*/

//因爲基類沒有默認構造函數，初始化列表第一列必須初始化基類
EPollPoller::EPollPoller(EventLoop *loop) : Poller(loop)
        , epollfd_(::epoll_create1(EPOLL_CLOEXEC))
        , events_(kInitEventListSize){
    if(epollfd_ < 0){
        LOG_FATAL("epoll_create error:%d \n", errno);
    }
}

EPollPoller::~EPollPoller(){
    ::close(epollfd_);
}

Timestamp EPollPoller::poll(int timeoutMs, ChannelList *activeChannels){
    //實際上用LOG_DEBUG更合理
    LOG_INFO("func = %s fd tatal = %d\n", _FUNCTION_, channels_.size());

    int numEvents = ::epoll_wait(epollfd_, &(*events_.begin()), static_cast<int>(events_.size()), timeoutMs);
    int saveErrno = errno;
    // Timestamp now(Timestamp::now());

    if(numEvents > 0){
        LOG_INFO("%d events hanppened\n", numEvents);
        fillActiveChannel(numEvents, activeChannels);
        if(numEvents == events_.size()){
            //需要擴容
            events_.resize(events_.size() * 2);
        }
        else if(numEvents == 0){
            //沒有發生事件，只是發生了超時
            LOG_INFO("%s timeout\n", _FUNCTION_);
        }
        else{
            //外部的中斷
            if(saveErrno != EINTR){
                //因爲中途有可能其他事件修改了errno，所以需要事先保存，並且在這裏恢復
                errno = saveErrno;
                LOG_ERROR("%s EPollPoller::poll() error", _FUNCTION_);
            }
        }
    }
    return Timestamp::now();
}




// channel update remove => EventLoop updateChannel removeChannel => Poller updateChannel removeChannel
/*
                    EventLoop
        ChannelList         Poller[只有註冊過的才會記錄到ChannelMap]
                          ChannelMap<fd, channel*> 
*/
void EPollPoller::updateChannel(Channel *channel){
    const int index = channel->index();
    LOG_INFO("func = %s fd = %d events = %d index = %d\n", _FUNCTION_, channel->fd(), channel->events(), index);
    if(index == kNew || index == kDeleted){
        if(index == kNew){
            //沒有添加過，添加
            int fd = channel->fd();
            channels_[fd] = channel;
        }
        channel->set_index(kAdded);
        update(EPOLL_CTL_ADD, channel);
    }
    else{
        //已經註冊過了
        int fd = channel->fd();
        if(channel->isNoneEvent()){
            //對任何事件都不感興趣
            update(EPOLL_CTL_DEL, channel);
            channel->set_index(kDeleted);
        }
        else{
            //還是對事件感興趣，但是感興趣的事件改變了
            update(EPOLL_CTL_MOD, channel);
        }
    }
}

void EPollPoller::removeChannel(Channel *channel){
    int fd = channel->fd();
    channels_.erase(fd);
    LOG_INFO("func = %s fd = %d events = %d index = %d\n", _FUNCTION_, channel->fd(), channel->events(), index);

    //如果註冊過，那還需要在epoll裏面刪除
    if(channel->index() == kAdded){
        update(EPOLL_CTL_DEL, channel);
    }
    channel->set_index(kNew);   
}

void EPollPoller::fillActiveChannel(int numEvents, ChannelList *activeChannels) const{
    for(int i = 0; i < numEvents; i++){
        Channel *channel = static_cast<Channel*>(events_[i].data.ptr);
        channel->set_revents(events_[i].events);
        //EventLoop就拿到了它的Poller給它返回的所有事件的channel列表
        activeChannels->push_back(channel);
    }
}

//epoll_ctl add/del/mod
void EPollPoller::update(int operation, Channel *channel){
    int fd = channel->fd();
    epoll_event ev;
    memset(&ev, 0, sizeof(ev));
    ev.data.ptr = channel;
    ev.data.fd = fd;
    ev.events = channel->events();
    
    if(::epoll_ctl(epollfd_, operation, fd, &ev) < 0){
        if(operation == EPOLL_CTL_DEL){
            //這種刪除失敗認爲是可以挽救的
            LOG_ERROR("epoll_ctl_del error error = %d \n", errno);
        }
        else{
            //添加或者修改，這種認爲是致命錯誤，會進行退出
            LOG_FATAL("epoll_ctl_ add/mod error error = %d \n", errno);
        }
    }
}